#include "client_vertexbuffer9.h"
#include <assert.h>
#include <stdlib.h>

#define INT_COMPRESS
//#define FLOAT_COMPRESS
#define COMPRESS_TO_DWORD
// #define COMPRESS_TO_BYTE

ClientVertexBuffer9::ClientVertexBuffer9(IDirect3DVertexBuffer9* ptr, int _length): m_vb(ptr), length(_length), isFirst(true) {
	Log::log("clientVertexBuffer constructor entered, len:%d\n", _length);
	//this->vb_data = (char *)malloc(sizeof(char ) * _length);
	if(!this->vb_data){
		Log::log("clientVertexBuffer constructor malloc vb_data failed!\n");
	}
}

HRESULT ClientVertexBuffer9::Lock(UINT OffestToLock, UINT SizeToLock, DWORD Flags) {
	m_LockData.OffsetToLock = OffestToLock;
	m_LockData.SizeToLock = SizeToLock;
	m_LockData.Flags = Flags;
	if(SizeToLock == 0) m_LockData.SizeToLock = length - OffestToLock;

	return S_OK;
}

HRESULT ClientVertexBuffer9::Unlock() {

	m_LockData.OffsetToLock = cc.read_uint();
	m_LockData.SizeToLock = cc.read_uint();
	m_LockData.Flags = cc.read_uint();

	Log::log("ClientVertexBuffer9::Unlock(), OffestToLock=%d, SizeToLock=%d Bytes, Flags=%d\n", m_LockData.OffsetToLock, m_LockData.SizeToLock, m_LockData.Flags);

	UpdateVertexBuffer();

	return D3D_OK;
}
int DeCompressTangentTo(unsigned long src, float * dst){
	//unsigned char * p = (unsigned char *)&src;

	unsigned long tmp = src;
	short tm = 0;
	tm = (short)((tmp&0xffc00000)>>22);
	*dst = (tm) * 2.0f / 1023.0f - 1.0f;
	dst ++;
	tm = (short)((tmp&0x003ff000)>>12);
	*dst = (tm) * 2.0f / 1023.0f - 1.0f;
	dst ++;
	tm = (short)((tmp&0x00000ffc)>>2);
	*dst = (tm) * 2.0f / 1023.0f - 1.0f;
	dst ++;

	tm = (short)((tmp&0x00000003));
	float a = 0.0;
	if(tm|0x00000001){
		a  = 1.0;
	}
	else{
		a = 0.0;
	}
	if( tm|0x00000002){
		a = -a;
	}else{

	}
	*dst = a;
	dst++;

	return 1;
}
int DecompressNormalTo(unsigned long src, float * dst){
	unsigned long tmp = src;
	short tm = 0;
	float * ff = dst;
	tm = (short)((tmp&0xffc00000)>>22);
	*ff = (tm) * 2.0f / 1023.0f - 1.0f;
	ff ++;
	tm = (short)((tmp&0x003ff000)>>12);
	*ff = (tm) * 2.0f / 1023.0f - 1.0f;
	ff ++;
	tm = (short)((tmp&0x00000ffc)>>2);
	*ff = (tm) * 2.0f / 1023.0f - 1.0f;
	ff ++;
	return 1;
}
int DecompressPositionTo(unsigned long src, float * dst, float vertex_max){
	unsigned long tmp = src;
	short tm = 0;
	tm = (short)((tmp&0xffc00000)>>22);
	*dst = ((tm) * 2.0f / 1023.0f - 1.0f)*vertex_max;
	dst ++;
	tm = (short)((tmp&0x003ff000)>>12);
	*dst = ((tm) * 2.0f / 1023.0f - 1.0f)*vertex_max;
	dst ++;
	tm = (short)((tmp&0x00000ffc)>>2);
	*dst = ((tm) * 2.0f / 1023.0f - 1.0f) *vertex_max;
	dst ++;
	return 1;
}
int DecompressPositionTo(unsigned long src, float * dst, float maxX, float maxY, float maxZ){
	unsigned long tmp = src;
	float * ff = dst;
	unsigned short tm = 0;
	tm = (unsigned short)((tmp&0xffc00000)>>22);
	*ff = ((tm) * 2.0f / 1023.0f - 1.0f)*maxX;
	Log::log(" generated %f\n", *dst);
	ff ++;
	tm = (unsigned short)((tmp&0x003ff000)>>12);
	*ff = ((tm) * 2.0f / 1023.0f - 1.0f)*maxY;
	ff ++;
	tm = (unsigned short)((tmp&0x00000fff));
	*ff = ((tm) * 2.0f / 1023.0f - 1.0f) *maxZ;
	ff ++;
	return 1;
}
int DecompressByteToFloat(BYTE src, float *dst){
	int ret = 0; 
	*(dst) = src * 2.0f / 255.0f - 1.0f;
	Log::log("Decompress byte:%g\n", *dst);
	return ret;
}
HRESULT ClientVertexBuffer9::s_Unlock(){
	m_LockData.OffsetToLock = cc.read_uint();
	m_LockData.SizeToLock = cc.read_uint();
	m_LockData.Flags = cc.read_uint();

	//v_count = cc.read_short();

	Log::log("ClientVertexBuffer9::Unlock(), OffestToLock=%d, SizeToLock=%d Bytes, Flags=%d\n", m_LockData.OffsetToLock, m_LockData.SizeToLock, m_LockData.Flags);

	s_UpdateVertexBuffer();

	return D3D_OK;
}

IDirect3DVertexBuffer9* ClientVertexBuffer9::GetVB() {
	return m_vb;
}

int ClientVertexBuffer9::GetLength() {
	return length;
}
float maxX = 0.0, maxY = 0.0, maxZ=  0.0;
void ClientVertexBuffer9::UpdateVertexBuffer() {
	char* vb_ptr;

	if(isFirst) {
		m_vb->Lock(0, 0, (void**)&vb_ptr, m_LockData.Flags);
		memset(vb_ptr, 0, length);
		m_vb->Unlock();
		isFirst = false;
	}

	m_vb->Lock(m_LockData.OffsetToLock, m_LockData.SizeToLock, (void**)&vb_ptr, D3DLOCK_NOOVERWRITE);//m_LockData.Flags);

	int md = cc.read_int();

	Log::log("ClientVertexBuffer9::UpdateVertexBuffer(), md=%d\n", md);

	if(md == CACHE_MODE_COPY) {
		Log::log(" CACHE_MODE_COPY\n");
#ifdef COMPRESS_TO_DWORD
		unsigned short flag = cc.read_ushort();   // the format flag
		UCHAR p_off = cc.read_uchar();
		UCHAR n_off = cc.read_uchar();
		UCHAR t_off = cc.read_uchar();
		UCHAR tt_off = cc.read_uchar();

		UCHAR tt_size = cc.read_uchar();
		UCHAR _stride = cc.read_uchar();
		UCHAR c_off = cc.read_uchar();
		UCHAR c_size = cc.read_uchar();

		//UCHAR streamOff = cc.read_uchar();
		UCHAR streamOff = 0;
		/*p_off -= streamOff;
		n_off -= streamOff;
		t_off -= streamOff;
		tt_off -= streamOff;*/

		this->stride = _stride;

		Log::log("ClientVertexBuffer9::UpdateVertexBuffer(), flag:%d, p_off:%d, n_off:%d, t_off:%d, tt_off:%d, tt_size:%d, stride:%d, streamOff:%d c_off:%d, c_size:%d\n", flag, p_off, n_off, t_off, tt_off, tt_size, _stride,streamOff, c_off, c_size);
		
		if( (flag & 1)){
			// position in this vb
			maxX = cc.read_float();
			maxY = cc.read_float();
			maxZ = cc.read_float();
			Log::log("maxX maxY maxZ: %f %f %f\n", maxX, maxY, maxZ);
		}

		// decompress the vb data
		DWORD tm = 0;
		//char * tmp = vb_data;
		char * tmp = vb_ptr;
		Log::log("ClientVertexBuffer9::UpdateVertexBuffer(), len:%d, stride:%d\n", this->GetLength(), _stride);
		for( int j = 0; j< this->GetLength(); j+= _stride){
			//Log::log("vb decompression for vertex:%d\n", j);
			if(flag & 1){
				// decompress position
				tm = cc.read_uint();
				DecompressPositionTo(tm, (float *)(tmp + p_off),maxX, maxY, maxZ);
				Log::log("dword:%u x y z:%g %g %g\n",tm, *(float *)(tmp + p_off), *(float *)(tmp + p_off + 4), *(float *)(tmp+ p_off + 8));
				//tmp+=12;
			}
			if(flag & 2){
				// decompress normal
				tm = cc.read_uint();
				DecompressNormalTo(tm, (float *)(tmp + n_off));
				//tmp+=12;
			}
			if( flag & 4){
				// decompress tangent
				tm = cc.read_uint();
				DeCompressTangentTo(tm, (float *)(tmp + t_off));
				//tmp+=16;
			}
			if(flag & 8){
				// decompress texture
				BYTE sr = 0;
				for( int m = 0; m < tt_size; m+=4){
#if 1
					sr = cc.read_uchar();
					DecompressByteToFloat(sr, (float *)(tmp + tt_off +m ));
#else
					*(float *)(tmp + tt_off +m ) = cc.read_float();
#endif
					//tmp+=4;
				}
				//tmp += tt_size;

			}
			if( flag & 16){
				cc.read_byte_arr(tmp + c_off, c_size);
				//tmp += c_size;
			}
			//vb_data += _stride;
			tmp += _stride;
		}
		Log::log(" CACHE_MODE_COPY end\n");
		Log::log("write the decompressed data to destination, len:%d, size to lock:%d\n", this->GetLength(),m_LockData.SizeToLock);
		//memcpy(vb_ptr, vb_data, this->GetLength());
#else
		//memcpy((char*)vb_ptr, cur_ptr, m_LockData.SizeToLock);
		cc.read_byte_arr((char*)vb_ptr, m_LockData.SizeToLock);
#endif
#ifdef COMPRESS_TO_BYTE

#endif
		Log::log("to unlock the vertex buffer\n");
		m_vb->Unlock();
		return;
	}

	if(md == CACHE_MODE_COM) {
		int in_len = cc.read_int();
		int out_len = 0;
		//Compressor::lzo_decode(cur_ptr, in_len, (char*)vb_ptr, out_len);
		cc.read_byte_arr((char*)vb_ptr, in_len);

		m_vb->Unlock();
		return;
	}

	if(md == CACHE_MODE_PRESS_NORMAL_TANGENT){
		// indicate that full vertex buffer should be update and normal and tangent is compress to DWORD
	}

	int last = 0;
	while(true) {
		//Compressor::decode1(cur_ptr, size, d);
		//cur_ptr += size;
		int d = cc.read_int();
		//Log::log("ClientVertexBuffer9::UpdateVertexBuffer, last=%d, d=%d size=%d bytes\n", last, d, size);
		if(d == (1<<28)-1) break;
		last += d;
		*((char*)(vb_ptr) + last) = cc.read_char();
	}

	HRESULT hr = m_vb->Unlock();
}
void ClientVertexBuffer9::s_UpdateVertexBuffer() {
	void* vb_ptr;


	if(isFirst) {
		m_vb->Lock(0, 0, (void**)&vb_ptr, m_LockData.Flags);
		memset(vb_ptr, 0, length);
		m_vb->Unlock();
		isFirst = false;
	}

	m_vb->Lock(m_LockData.OffsetToLock, m_LockData.SizeToLock, (void**)&vb_ptr, D3DLOCK_NOOVERWRITE);//m_LockData.Flags);

	int md = cc.read_int();
	short  vb_count= cc.read_short();
	float max_vertex = cc.read_float();

	Log::log("ClientVertexBuffer9::UpdateVertexBuffer(), md=%d, vb count:%d max vertex value:%f\n", md,vb_count,max_vertex);

	if(md == CACHE_MODE_COPY) {

		//memcpy((char*)vb_ptr, cur_ptr, m_LockData.SizeToLock);
		cc.read_byte_arr((char*)vb_ptr, m_LockData.SizeToLock);
		/*
		unsigned long tm = 0;
		char * po = (char *)vb_ptr;
		// test for vb 779, special operation to fullfill vertex buffer
		for(int i = 0; i< vb_count; i++){
		// fullfill 12 bytes( position)
		tm = cc.read_uint();
		DecompressPositionTo(tm,(float *)po, max_vertex);

		// fullfill normal, unsigned long to 3 floats
		tm = cc.read_uint();
		po+=12;
		DecompressNormalTo(tm,(float *)po);
		po+=12;

		// fullfill tangent, unsigned long to 4 floats
		tm = cc.read_uint();
		DecompressNormalTo(tm, (float *)po);
		po+=16;
		}
		*/
		m_vb->Unlock();
		return;
	}

	if(md == CACHE_MODE_COM) {
		int in_len = cc.read_int();
		int out_len = 0;
		//Compressor::lzo_decode(cur_ptr, in_len, (char*)vb_ptr, out_len);
		cc.read_byte_arr((char*)vb_ptr, in_len);

		m_vb->Unlock();
		return;
	}

	if(md == CACHE_MODE_PRESS_NORMAL_TANGENT){
		// indicate that full vertex buffer should be update and normal and tangent is compress to DWORD
	}

	int last = 0;
	while(true) {
		//Compressor::decode1(cur_ptr, size, d);
		//cur_ptr += size;
		int d = cc.read_int();
		//Log::log("ClientVertexBuffer9::UpdateVertexBuffer, last=%d, d=%d size=%d bytes\n", last, d, size);
		if(d == (1<<28)-1) break;
		last += d;
		*((char*)(vb_ptr) + last) = cc.read_char();
	}

	HRESULT hr = m_vb->Unlock();
}
